package com.dollar.juc.current_limit.guava_limit.impl;


import java.util.concurrent.TimeUnit;

/**
 * 实现 1/秒 的令牌桶
 */
class MyRateLimiter {
    //下一令牌产生时间
    long next = System.nanoTime();
    //发放令牌间隔：纳秒
    long interval = 1000_000_000;

    /**
     * 预占令牌，返回能够获取令牌的时间
     * 如果预占时刻有令牌，不用等待直接获取令牌，更新下一令牌发放时间为 now + interval
     * 否则 next>now，那就 next时获取令牌，需要等 next-now， 更新下一令牌发放时间为 next + interval
     *
     * @param now 申请令牌时的时间
     * @return
     */
    synchronized long reserve(long now) {
        //请求时间在下一令牌产生时间之后
        //重新计算下一令牌产生时间
        if (now > next) {
            //将下一令牌产生时间重置为当前时间
            next = now;
        }
        //能够获取令牌的时间
        long at = next;
        //设置下一令牌产生时间
        next += interval;
        //返回线程需要等待的时间
        return Math.max(at, 0L);
    }

    //申请令牌
    void acquire() {
        //申请令牌时的时间
        long now = System.nanoTime();
        //预占令牌
        long at = reserve(now);
        long waitTime = Math.max(at - now, 0);
        //按照条件等待
        if (waitTime > 0) {
            try {
                TimeUnit.NANOSECONDS.sleep(waitTime);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
